Fix ApiCallDuration so that it measures the whole API call - #7338
Conversation
|
With this change, the ApiCallDuration could be longer than configured apiCallTimeout since marshalling and endpoint resolution stuff does not count into the timeout. This could be confusing and worth mentioning that in somewhere, probably API_CALL_DURATION javadoc. |
Great catch and callout! I've updated javadoc in both spots. I'd love to fix that as well, as I think the current arrangement of it is misleading, but I think thats a larger breaking change because it could cause apiCallTimeout configurations that were previously allowing requests to succeed could cause requests to start failing (since it makes the timeout itself more strict). |
Yeh I agree. I think a doc reminder is enough and we already called out that apiCallTimeout excludes marshaling, so although that is misleading, that is a documented behavior rather than a bug. |
|
This pull request has been closed and the conversation has been locked. Comments on closed PRs are hard for our team to see. If you need more assistance, please open a new issue that references this one. |
Fix
ApiCallDurationso it measures the whole API callMotivation and Context
CoreMetric.API_CALL_DURATIONunderstated latency, and did so differently on the sync and async clients.The measurement lived in a request pipeline stage. The pipeline's input is the already-marshalled request, so the window
opened after marshalling had finished — contradicting the formula in the metric's own javadoc, which lists a
MARSHALLING_DURATIONterm.The async client was worse.
AsyncApiCallMetricCollectionStagewas nested one builder deeper than its sync counterpart,so the window also excluded the entire request-mutation chain: endpoint resolution, auth scheme resolution, request compression, checksums, header and query merging and user agent.
The two clients also disagreed on the
afterExecutioninterceptors: inside the window on async, outside on sync.Modifications
ApiCallDurationis moved out of the pipeline entirely, up into the client handlers alongsideAPI_CALL_SUCCESSFUL— which was already scoped to the whole call, so the two metrics had been reporting on different windows from the same collector. No pipeline arrangement can enclose marshalling, so this was the only available position.afterExecutioninterceptors are now inside the window on both clients: an interceptor running as part of the call is part of the call.SERVICE_ENDPOINTcollection moved into the handlers'doExecute, the first point after the pipeline that still holds theExecutionContext.NoOpMetricCollector, the default when no publisher is configured, so the no-metrics path does not regress.Testing
New
ApiCallDurationWindowTest(test/codegen-generated-classes-test) injects a 300 ms delay into one phase at a time and asserts it appears inApiCallDuration, for both clients.Types of changes
Checklist
mvn installsucceedsscripts/new-changescript and following the instructions. Commit the new file created by the script in.changes/next-releasewith your changes.License
One internal-API behaviour change: code driving
AmazonSyncHttpClientorAmazonAsyncHttpClientdirectly, bypassing theclient handlers, no longer receives
ApiCallDurationorSERVICE_ENDPOINT.Testing
New
ApiCallDurationWindowTest(test/codegen-generated-classes-test) injects a 300 ms delay into one phase at a timeand asserts it appears in
ApiCallDuration, for both clients. This is deliberately stronger than asserting theadditivity formula over real timings: the phases at issue cost microseconds against a millisecond-scale call, so an
inequality passes whether or not they are counted — an additivity-only check would not have caught the sync defect.
Verified against the unpatched SDK: 5 of the 7 cases fail, and the 2 that pass are exactly the two that were already
correct (sync endpoint resolution, async
afterExecution).Also added:
ApiCallDurationAssertions, applied fromCoreMetricsTestandBaseAsyncCoreMetricsTest, assertingApiCallDurationencloses each component metric on success, error and retry paths.